using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace RacerGame
{
    class TextureTools
    {
        private static GraphicsDevice device;
        private static SpriteBatch spriteBatch;
		public static SpriteBatch SpriteBatch { get { return spriteBatch; } }
		private static volatile bool drawInProgress = false;
		public static bool DrawInProgress { get { return drawInProgress; } }
        private static RenderTarget2D currentRenderTarget;
        public static Texture2D Point;        
        public static Texture2D Gauss;
        public static Texture2D FilledCircle;
        public static Texture2D Circle;
        private const int textureSize = 64;
        private const int circleTextureSize = 1024;
        private static bool Initialized
        {
            get
            {
                return (device != null);
            }
        }
        static public void Init(GraphicsDevice device)
        {
            TextureTools.device = device;
            spriteBatch = new SpriteBatch(device);
           
            //genereate point texture
            Point = new Texture2D(device, 1, 1, 0, TextureUsage.None, SurfaceFormat.Color);
            Color[] pointList = {Color.White};
            Point.SetData<Color>(pointList);

            // generate gauss texture
            Gauss = new Texture2D(device, textureSize, textureSize,1, TextureUsage.AutoGenerateMipMap,SurfaceFormat.Color);
            Color[] gaussList = new Color[textureSize*textureSize];
            Vector2 center = new Vector2(textureSize / 2, textureSize / 2);
            float tempSize = 204.80426f;
            //do
            //{
            //tempSize += 0.1f;
            for (int x = 0; x < textureSize; x++)
            {
                for (int y = 0; y < textureSize; y++)
                {
                    float distSquared;
                    Vector2 coord = new Vector2(x + 0.5f, y + 0.5f);
                    Vector2.DistanceSquared(ref coord, ref center, out distSquared);
                    byte value = (byte) (255 * Math.Exp(-distSquared / tempSize));
                    gaussList[x + y * textureSize] = new Color(255, 255, 255, value);
                }
            }
            //} while (gaussList[textureSize / 2].R <= 1);
            Gauss.SetData<Color>(gaussList);

            // generate circle texture
            FilledCircle = new Texture2D(device, circleTextureSize, circleTextureSize, 1, TextureUsage.AutoGenerateMipMap, SurfaceFormat.Color);
            Circle = new Texture2D(device, circleTextureSize, circleTextureSize, 1, TextureUsage.AutoGenerateMipMap, SurfaceFormat.Color);
            Color[] filledCircleList = new Color[circleTextureSize * circleTextureSize];
            Color[] circleList = new Color[circleTextureSize * circleTextureSize];
            center = new Vector2(circleTextureSize / 2, circleTextureSize / 2);
            for (int x = 0; x < circleTextureSize; x++)
            {
                for (int y = 0; y < circleTextureSize; y++)
                {
                    float dist;
                    Vector2 coord = new Vector2(x + 0.5f, y + 0.5f);
                    Vector2.Distance(ref coord, ref center, out dist);
                    byte value = (byte)(255 - MathHelper.SmoothStep(0, 255, dist - ((circleTextureSize / 2) - 1)));
                    byte innerValue = (byte)(MathHelper.SmoothStep(0, 255, dist - ((circleTextureSize / 2) - (1 + 16))));
                    filledCircleList[x + y * circleTextureSize] = new Color(255, 255, 255, value);
                    circleList[x + y * circleTextureSize] = new Color(255, 255, 255, (byte)(((int)value + (int)innerValue) - 255));
                }
            }
            FilledCircle.SetData<Color>(filledCircleList);
            Circle.SetData<Color>(circleList);
        }
        static public void Begin(RenderTarget2D renderTarget)
        {
            if (drawInProgress)
            {
                throw new Exception("TextureTools.Draw already in progress.");
            }
            currentRenderTarget = renderTarget;
            device.SetRenderTarget(0, currentRenderTarget);
            device.Clear(ClearOptions.Target,new Color(0,0,0,0),1,0);
            spriteBatch.Begin(SpriteBlendMode.Additive, SpriteSortMode.Deferred, SaveStateMode.SaveState);
            drawInProgress = true;
        }
        static public void DrawTexture(Texture2D texture, float opacity) {
            if (opacity < 0 || opacity > 1)
            {
                throw new Exception("opacity must be between 0 and 1");
            }            
            //new Color((byte)(255 * opacity), (byte)(255 * opacity), (byte)(255 * opacity), (byte)(255 * opacity))
            spriteBatch.Draw(texture, new Vector2(0, 0), new Color(255, 255, 255, (byte)(255 * opacity)));
        }
        static public void DrawPoint(int x, int y, Color color)
        {
            spriteBatch.Draw(Point, new Vector2(x, y), color);
        }
        static public void DrawSquare(int x, int y, int size, Color color)
        {
            Rectangle dest = new Rectangle((int) (x - (size / 2.0f)), (int) (y - (size / 2.0f)), size, size);
            spriteBatch.Draw(Point, dest, color);
        }
        static public void DrawGauss(int x, int y, int size, Color color)
        {
            Rectangle dest = new Rectangle((int)(x - (size / 2.0f)), (int)(y - (size / 2.0f)), size, size);
            spriteBatch.Draw(Gauss, dest, color);
        }
        static public void DrawRepeatingGauss(int x, int y, int size, Color color)
        {
            int Width = currentRenderTarget.Width;
            int Height = currentRenderTarget.Height;
            int halfSize = size / 2;
            DrawGauss(x, y, size, color);
            if (x - halfSize < 0)
            {
                DrawGauss(x + Width, y, size, color);
                if (y - halfSize < 0)
                {
                    DrawGauss(x + Width, y + Height, size, color);
                    DrawGauss(x, y + Height, size, color);
                }
                else if (y + halfSize >= Height)
                {
                    DrawGauss(x + Width, y - Height, size, color);
                    DrawGauss(x, y - Height, size, color);
                }
            }
            else if (x + halfSize >= Width)
            {
                DrawGauss(x - Width, y, size, color);
                if (y - halfSize < 0)
                {
                    DrawGauss(x - Width, y + Height, size, color);
                    DrawGauss(x, y + Height, size, color);
                }
                else if (y + halfSize >= Height)
                {
                    DrawGauss(x - Width, y - Height, size, color);
                    DrawGauss(x, y - Height, size, color);
                }
            }
            else
            {
                if (y - halfSize < 0)
                {
                    DrawGauss(x, y + Height, size, color);
                }
                else if (y + halfSize >= Height)
                {
                    DrawGauss(x, y - Height, size, color);
                }
            }
        }
        static public void DrawFilledCircle(int x, int y, int size, Color color)
        {
            Rectangle dest = new Rectangle((int)(x - (size / 2.0f)), (int)(y - (size / 2.0f)), size, size);
            spriteBatch.Draw(FilledCircle, dest, color);
        }
        static public void DrawRectangle(Rectangle rectangle, Color color)
        {
            spriteBatch.Draw(Point, rectangle, color);
        }
        static public Texture2D End()
        {
            spriteBatch.End();
            device.SetRenderTarget(0, null);
            drawInProgress = false;
		    device.Textures[0] = null;
            return currentRenderTarget.GetTexture();
        }

        public abstract class AnimatedTexture
        {
            protected RenderTarget2D renderTarget;
            public Texture2D Texture;
            public readonly int Width;
            public readonly int Height;
            public AnimatedTexture(int width,int height)
            {
                this.Width = width;
                this.Height = height;
                renderTarget = new RenderTarget2D(device, Width, Height,1, SurfaceFormat.Color, MultiSampleType.None, 0);
                Texture = new Texture2D(device, Width, Height, 1, TextureUsage.AutoGenerateMipMap, SurfaceFormat.Color);
            }
            public abstract void Update();
        }
        public class MovingSpots : AnimatedTexture
        {
            internal class Spot
            {
                public Vector2 Position;
                public Vector2 Speed;
                public Color Color;
                public Spot(Vector2 position, Vector2 speed, Color color)
                {
                    this.Position = position;
                    this.Speed = speed;
                    this.Color = color;
                }
                public Spot()
                {
                    this.Position = Vector2.Zero;
                    this.Speed = Vector2.Zero;
                    this.Color = Color.White;
                }
                public void Move(int width, int height)
                {
                    Position += Speed;
                    if (Position.X < 0)
                    {
                        Position.X += width;
                    }
                    else if (Position.X > width)
                    {
                        Position.X -= width;
                    }
                    if (Position.Y < 0)
                    {
                        Position.Y += height;
                    }
                    else if (Position.Y > height)
                    {
                        Position.Y -= height;
                    }
                }
            }
            private Spot[] spotList;
            private int frameCounter = 0;
            private int updateInterval = 1;
            public MovingSpots(int width, int height, int numberOfSpots) : base(width, height)
            {
                Random random = new Random();
                spotList = new Spot[numberOfSpots];
                for (int i = 0; i < numberOfSpots; i++)
                {
                    spotList[i] = new Spot( new Vector2(random.Next(width), random.Next(height)),
                        new Vector2((float)(2f * random.NextDouble()) - 1f, (float)(2f * random.NextDouble()) - 1f),
                        (random.NextDouble() > 1 / 3.0f ? (random.NextDouble() > 1 / 2.0f ? Color.Red : Color.Blue) : Color.Lime));
                    spotList[i].Move(width, height);
                }
            }
            public override void Update()
            {
                if (++frameCounter == updateInterval)
                {
                    frameCounter = 0;
                    Begin(renderTarget);
                    foreach (Spot spot in spotList)
                    {
                        spot.Move(Width, Height);
                        DrawRepeatingGauss((int)spot.Position.X, (int)spot.Position.Y, 32, spot.Color);
                    }
                    Texture = End();
                }
            }
        }
    }
}
